//
//  RCTBaiduMapView.m
//  YuanXinBaiduMap
//
//  Created by 晏德智 on 16/9/26.
//  Copyright © 2016年 晏德智. All rights reserved.
//

#import "RCTBaiduMapView.h"
#import "RCTBaiduMapAnnotation.h"
#import "RCTBaiduMapOverlay.h"
#import <BaiduMapAPI_Utils/BMKGeometry.h>
#import <BaiduMapAPI_Search/BMKGeocodeSearch.h>
#import "BaiduLocationKitService.h"

const CLLocationDegrees RCTBaiduMapDefaultSpan = 0.005;
const NSTimeInterval RCTBaiduMapRegionChangeObserveInterval = 0.1;
const CGFloat RCTBaiduMapZoomBoundBuffer = 0.01;

@interface ZoomControlView : UIView {
    BMKMapStatus *_mapStatus;//百度地图状态
    UIButton *_zoomOut, *_zoomIn;
    UIView *_line;
    CGFloat _maxLevel;
    CGFloat _minLevel;
}

@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, weak) RCTBaiduMapView *mapView;

@end

@implementation ZoomControlView

- (instancetype)init
{
    self = [super init];
    if (self) {
        [self initZoomControlView];
    }
    return self;
}

- (void)initZoomControlView {
    CGFloat width = 40;
    CGFloat height = width*2+1;
    CGFloat padding = 2;
    //content view
    _contentView = [UIView new];
    _contentView.frame = (CGRect){
        CGPointZero,
        CGSizeMake(width, height),
    };
    _contentView.backgroundColor = [UIColor whiteColor];
    _contentView.layer.cornerRadius = 2;
    [self addSubview:_contentView];
    //zoom in
    _zoomIn = [UIButton buttonWithType:UIButtonTypeCustom];
    [_zoomIn setImage:[UIImage imageNamed:@"yuanxinBaiduMap.bundle/images/main_icon_zoomin.png"] forState:UIControlStateNormal];
    _zoomIn.imageEdgeInsets = UIEdgeInsetsMake(10,10,10,10);//设置image在button上的位置（上top，左left，下bottom，右right）这里可以写负值，对上写－5，那么image就象上移动5个像素
    [_zoomIn setFrame:CGRectMake(0, 0, width, width)];
    [_zoomIn addTarget:self action:@selector(zoomIn:) forControlEvents:UIControlEventTouchUpInside];
    [_contentView addSubview:_zoomIn];
    //line
    _line = [UIView new];
    _line.frame = CGRectMake(padding, width, width-2*padding, 1);
    _line.backgroundColor = [UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:0.6];
    [_contentView addSubview:_line];
    // zoom out
    _zoomOut = [UIButton buttonWithType:UIButtonTypeCustom];
    [_zoomOut setImage:[UIImage imageNamed:@"yuanxinBaiduMap.bundle/images/main_icon_zoomout.png"] forState:UIControlStateNormal];
    _zoomOut.imageEdgeInsets = UIEdgeInsetsMake(10,10,10,10);//设置image在button上的位置（上top，左left，下bottom，右right）这里可以写负值，对上写－5，那么image就象上移动5个像素
    [_zoomOut setFrame:CGRectMake(0, width+1, width, width)];
    [_zoomOut addTarget:self action:@selector(zoomOut:) forControlEvents:UIControlEventTouchUpInside];
    [_contentView addSubview:_zoomOut];
}

/**
 * set {@link MapView}
 */
-(void)setMapView:(RCTBaiduMapView *)mapView
{
    if(mapView) {
        _mapView = mapView;
        _mapStatus = mapView.getMapStatus;
        _maxLevel = mapView.maxZoomLevel;
        _minLevel = mapView.minZoomLevel;
    }
}

//放大
-(void)zoomIn:(id)sender {
    if (_mapView) {
        [_mapView zoomIn];
        _mapStatus = _mapView.getMapStatus;
        [self refreshZoomControlView];
    }
}

//放大
-(void)zoomOut:(id)sender {
    if (_mapView) {
        [_mapView zoomOut];
        _mapStatus = _mapView.getMapStatus;
        [self refreshZoomControlView];
    }
}

-(void)refreshZoomControlView {

    CGFloat zoom = _mapStatus.fLevel;

    if(zoom > _minLevel && zoom < _maxLevel)
    {

        if(!_zoomIn.isEnabled){
            [_zoomIn setEnabled:YES]; //设置为可点击
        }

        if(!_zoomOut.isEnabled){
            [_zoomOut setEnabled:YES]; //设置为可点击
        }

    } else if (zoom == _minLevel)
    {
        [_zoomOut setEnabled:NO];
        [_zoomIn setEnabled:YES];
    } else
    {
        [_zoomIn setEnabled:NO];
        [_zoomOut setEnabled:YES];
    }
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    CGFloat width = 40;
    CGFloat height = 81;
    CGFloat marginRight = 6;
    CGFloat marginBottom = 2;
    [self setFrame:CGRectMake(CGRectGetWidth(_mapView.frame) - width - marginRight, CGRectGetHeight(_mapView.frame) - height - marginBottom, width, height)];
}

@end

@implementation RCTBaiduMapAnnotationView

- (void)setContentView:(UIView *)contentView
{
    [_contentView removeFromSuperview];
    _contentView = contentView;
    [self addSubview:_contentView];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.bounds = (CGRect){
        CGPointZero,
        _contentView.frame.size,
    };
}

@end

@interface CustomBMKUserLocation : BMKUserLocation

- (instancetype)initWithBMKLocation:(BMKLocation *)userLocation andHeading:(CLHeading * _Nullable)heading;

@end

@implementation CustomBMKUserLocation

@synthesize updating = _updating;
@synthesize location = _location;
@synthesize heading = _heading;
@synthesize title = _title;
@synthesize subtitle = _subtitle;

- (instancetype)initWithBMKLocation:(BMKLocation *)userLocation andHeading:(CLHeading * _Nullable)heading
{
    self = [super init];
    if (self) {
        _updating = NO;
        _location = userLocation.location;
        _heading = heading;
    }
    return self;
}

@end

@interface RCTBaiduMapView ()<BMKMapViewDelegate, BMKGeoCodeSearchDelegate, CLLocationManagerDelegate>

@property (nonatomic, assign) NSUInteger locatingCount;
@property (nonatomic) ZoomControlView *zoomControlView;
@property (nonatomic, copy) RCTBubblingEventBlock onGeoCodeResult;
@property (nonatomic, copy) RCTBubblingEventBlock onReverseGeoCodeResult;
@property (copy) void (^requestAuthorizationHandler)(CLAuthorizationStatus);

@end

@implementation RCTBaiduMapView
{
    UIView *_legalLabel;
    CLLocationManager *_locationManager;
    BaiduLocationKitService *_locationService; // 采用 <BMKLocationkit/BMKLocationManager.h> 实现定位，封装在 BaiduLocationService 中
    BMKLocationViewDisplayParam *_myLocationViewParam;
    BMKGeoCodeSearch *_geocodesearch;
    BMKGeoCodeSearchOption *_geoCodeSearchOption;
    NSMutableArray<UIView *> *_reactSubviews;
    NSString *_previousAddress;
}

- (instancetype)init
{
    if ((self = [super init])) {
        _hasStartedRendering = NO;
        _reactSubviews = [NSMutableArray new];
        _previousAddress = @"";
        self.locatingCount = 0;
        self.delegate = self; // 自己实现代理

        for (UIView *subview in self.subviews) {
            if ([NSStringFromClass(subview.class) isEqualToString:@"MKAttributionLabel"]) {
                _legalLabel = subview;
                break;
            }
        }
    }
    return self;
}

- (void)dealloc
{
    [self invalidate];
}

- (void)invalidate {
    [self stopUserLocation];
    _geocodesearch.delegate = nil;
    _geocodesearch = nil;
    self.onDidChangeUserLocation = nil;
    self.onReverseGeoCodeResult = nil;
    self.onGeoCodeResult = nil;
    [_regionChangeObserveTimer invalidate];
    _zoomControlView = nil;
    [self setDelegate:nil];
    if (_locationManager) {
        _locationManager.delegate = nil;
        _locationManager = nil;
    }
}

- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
    [super insertReactSubview:subview atIndex:atIndex];
    for (UIView *item in _reactSubviews) {
        CGRect frame = item.frame;
        frame.origin.x = -999;
        [item setFrame:frame];
    }
    [_reactSubviews insertObject:subview atIndex:atIndex];
    // 为了解决 insertReactSubview 调用晚于 mapView:viewForAnnotation:
    // 因为不知道哪个加过了哪个没加过，所以粗暴的全部更新
    for (RCTBaiduMapAnnotation *item in self.annotations) {
        [self removeAnnotation:item];
        [self addAnnotation:item];
    }
    [subview setHidden:YES];
}

-(void)removeReactSubview:(UIView *)subview {
    [super removeReactSubview:subview];
    [_reactSubviews removeObject:subview];
}

- (NSArray<UIView *> *)reactSubviews
{
    return _reactSubviews;
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    if (_legalLabel) {
        dispatch_async(dispatch_get_main_queue(), ^{
            CGRect frame = _legalLabel.frame;
            if (_legalLabelInsets.left) {
                frame.origin.x = _legalLabelInsets.left;
            } else if (_legalLabelInsets.right) {
                frame.origin.x = self.frame.size.width - _legalLabelInsets.right - frame.size.width;
            }
            if (_legalLabelInsets.top) {
                frame.origin.y = _legalLabelInsets.top;
            } else if (_legalLabelInsets.bottom) {
                frame.origin.y = self.frame.size.height - _legalLabelInsets.bottom - frame.size.height;
            }
            _legalLabel.frame = frame;
        });
    }
}

#pragma mark - Accessors

- (void)setShowsUserLocation:(BOOL)showsUserLocation
{
    if (self.showsUserLocation != showsUserLocation) {

        if (showsUserLocation) {
            [self startUserLocation:[RCTConvert BaiduLocationOptions:@{@"distanceFilter":@(5), @"enableHighAccuracy": @(YES)}]];
        } else {
            [self stopUserLocation];
        }

        super.showsUserLocation = showsUserLocation;

    }
}

- (void)setShowsZoomControl:(BOOL)showsZoomControl {
    _showsZoomControl = showsZoomControl;
    if (showsZoomControl) {
        if (!_zoomControlView) {
            _zoomControlView = [[ZoomControlView alloc] init];
        }
        [_zoomControlView removeFromSuperview];
        [_zoomControlView setMapView:self];
        [self addSubview:_zoomControlView];
    } else {
        if (_zoomControlView) {
            [_zoomControlView removeFromSuperview];
        }
    }
}

- (void)setUserLocationViewParams:(BMKLocationViewDisplayParam *)userLocationViewParams
{
    if (self.userLocationViewParams != userLocationViewParams) {
        [self updateLocationViewWithParam:userLocationViewParams];
        _userLocationViewParams = userLocationViewParams;
    }
}

- (void)setRegion:(BMKCoordinateRegion)region animated:(BOOL)animated
{
    if (!CLLocationCoordinate2DIsValid(region.center)) {
        return;
    }

    if (!region.span.latitudeDelta) {
        region.span.latitudeDelta = self.region.span.latitudeDelta;
    }
    if (!region.span.longitudeDelta) {
        region.span.longitudeDelta = self.region.span.longitudeDelta;
    }

    [super setRegion:region animated:animated];
}

- (void)setAnnotations:(NSArray<RCTBaiduMapAnnotation *> *)annotations
{
    NSMutableArray<NSString *> *newAnnotationIDs = [NSMutableArray new];
    NSMutableArray<RCTBaiduMapAnnotation *> *annotationsToDelete = [NSMutableArray new];
    NSMutableArray<RCTBaiduMapAnnotation *> *annotationsToAdd = [NSMutableArray new];

    for (RCTBaiduMapAnnotation *annotation in annotations) {
        if (![annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
            continue;
        }

        [newAnnotationIDs addObject:annotation.identifier];

        if (![_annotationIDs containsObject:annotation.identifier]) {
            [annotationsToAdd addObject:annotation];
        }
    }
    for (RCTBaiduMapAnnotation *annotation in self.annotations) {
        if (![annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
            continue;
        }

        if (![newAnnotationIDs containsObject:annotation.identifier]) {
            [annotationsToDelete addObject:annotation];
        }
    }

    if (annotationsToDelete.count > 0) {
        [self removeAnnotations:(NSArray<id<BMKAnnotation>> *)annotationsToDelete];
    }

    if (annotationsToAdd.count > 0) {
        [self addAnnotations:(NSArray<id<BMKAnnotation>> *)annotationsToAdd];
    }

    self.annotationIDs = newAnnotationIDs;
    [self showAnnotations:self.annotations animated:YES];

    if (self.autoZoomToSpan) {
        [self zoomToSpan];
    }
}

- (void)setOverlays:(NSArray<RCTBaiduMapOverlay *> *)overlays
{
    NSMutableArray<NSString *> *newOverlayIDs = [NSMutableArray new];
    NSMutableArray<RCTBaiduMapOverlay *> *overlaysToDelete = [NSMutableArray new];
    NSMutableArray<RCTBaiduMapOverlay *> *overlaysToAdd = [NSMutableArray new];

    for (RCTBaiduMapOverlay *overlay in overlays) {
        if (![overlay isKindOfClass:[RCTBaiduMapOverlay class]]) {
            continue;
        }

        [newOverlayIDs addObject:overlay.identifier];

        if (![_overlayIDs containsObject:overlay.identifier]) {
            [overlaysToAdd addObject:overlay];
        }
    }

    for (RCTBaiduMapOverlay *overlay in self.overlays) {
        if (![overlay isKindOfClass:[RCTBaiduMapOverlay class]]) {
            continue;
        }

        if (![newOverlayIDs containsObject:overlay.identifier]) {
            [overlaysToDelete addObject:overlay];
        }
    }

    if (overlaysToDelete.count > 0) {
        [self removeOverlays:(NSArray<id<BMKOverlay>> *)overlaysToDelete];
    }
    if (overlaysToAdd.count > 0) {
        [self addOverlays:(NSArray<id<BMKOverlay>> *)overlaysToAdd];
    }

    self.overlayIDs = newOverlayIDs;

    if (self.autoZoomToSpan) {
        [self zoomToSpan];
    }
}

- (void)zoomToSpan:(NSArray<RCTBaiduMapAnnotation *> *)annotations andOverlays:(NSArray<RCTBaiduMapOverlay *> *)overlays
{
    CLLocationDegrees minLat = 0.0;
    CLLocationDegrees maxLat = 0.0;
    CLLocationDegrees minLon = 0.0;
    CLLocationDegrees maxLon = 0.0;
    BOOL hasInitialized = NO;
    NSInteger index = 0;
    if (annotations != nil) {
        for (RCTBaiduMapAnnotation *annotation in annotations) {
            if (index == 0 && hasInitialized == NO) {
                minLat = maxLat = annotation.coordinate.latitude;
                minLon = maxLon = annotation.coordinate.longitude;
                hasInitialized = YES;
            } else {
                minLat = MIN(minLat, annotation.coordinate.latitude);
                minLon = MIN(minLon, annotation.coordinate.longitude);
                maxLat = MAX(maxLat, annotation.coordinate.latitude);
                maxLon = MAX(maxLon, annotation.coordinate.longitude);
            }
            index ++;
        }
    }
    index = 0;
    if (overlays != nil) {
        for (RCTBaiduMapOverlay *overlay in overlays) {
            for (NSInteger i = 0; i < overlay.pointCount; i++) {
                BMKMapPoint pt = overlay.points[i];
                CLLocationCoordinate2D coordinate = BMKCoordinateForMapPoint(pt);
                if (index == 0 && i == 0 && hasInitialized == NO) {
                    minLat = maxLat = coordinate.latitude;
                    minLon = maxLon = coordinate.longitude;
                    hasInitialized = YES;
                } else {
                    minLat = MIN(minLat, coordinate.latitude);
                    minLon = MIN(minLon, coordinate.longitude);
                    maxLat = MAX(maxLat, coordinate.latitude);
                    maxLon = MAX(maxLon, coordinate.longitude);
                }
            }
            index ++;
        }
    }

    if (hasInitialized) {
        CLLocationCoordinate2D center;
        center.latitude = (maxLat + minLat) * .5f;
        center.longitude = (minLon + maxLon) * .5f;
        BMKCoordinateSpan span = BMKCoordinateSpanMake(maxLat - minLat + 0.02, maxLon - minLon + 0.02);

        BMKCoordinateRegion region = BMKCoordinateRegionMake(center, span);

        [self setRegion:region animated:YES];
    }
}

- (void)zoomToSpan
{
    [self zoomToSpan:self.annotations andOverlays:self.overlays];
}

- (void)zoomToSpan:(NSArray<CLLocation *> *)locations
{
    if (locations == nil || locations.count == 0) {
        [self zoomToSpan];
    } else if (locations.count == 1) {
        CLLocation *onlyLocation = locations.firstObject;
        [self zoomToCenter:onlyLocation.coordinate];
    } else {
        CLLocationDegrees minLat = 0.0;
        CLLocationDegrees maxLat = 0.0;
        CLLocationDegrees minLon = 0.0;
        CLLocationDegrees maxLon = 0.0;
        NSInteger index = 0;
        for (CLLocation *location in locations) {
            if (index == 0) {
                minLat = maxLat = location.coordinate.latitude;
                minLon = maxLon = location.coordinate.longitude;
            } else {
                minLat = MIN(minLat, location.coordinate.latitude);
                minLon = MIN(minLon, location.coordinate.longitude);
                maxLat = MAX(maxLat, location.coordinate.latitude);
                maxLon = MAX(maxLon, location.coordinate.longitude);
            }
            index ++;
        }

        CLLocationCoordinate2D center;
        center.latitude = (maxLat + minLat) * .5f;
        center.longitude = (minLon + maxLon) * .5f;
        BMKCoordinateSpan span = BMKCoordinateSpanMake(maxLat - minLat + 0.02, maxLon - minLon + 0.02);

        BMKCoordinateRegion region = BMKCoordinateRegionMake(center, span);

        [self setRegion:region animated:YES];

    }
}

- (void)zoomToCenter:(CLLocationCoordinate2D)coordinate
{
    BMKMapStatus *newMapStatus = [BMKMapStatus new];
    newMapStatus.targetGeoPt = coordinate;
    newMapStatus.fLevel = 16;

    [self setMapStatus:newMapStatus withAnimation:YES];
}

#pragma mark - private methods

- (void)sendBMKLocationUserLocation:(BMKLocation *)userLocation
{
    if(self.onDidChangeUserLocation){
        self.onDidChangeUserLocation([self locationInfoWithBMKLocation:userLocation]);
    }
}

- (void)sendUserLocation:(BMKUserLocation *)userLocation isHeading:(BOOL)isHeading
{
    if (isHeading) {
        if(self.onDidChangeUserLocation){
            self.onDidChangeUserLocation([self locationInfoWithLocation:userLocation andAddress:_previousAddress]);
        }
        return;
    }
    //__weak typeof (self) weakSelf = self;
    __unsafe_unretained typeof (self) weakSelf = self;
    if (!self.onReverseGeoCodeResult) {
        self.onReverseGeoCodeResult = ^(NSDictionary *body) {
            NSString *address = body[@"address"];
            _previousAddress = address;
            if(weakSelf.onDidChangeUserLocation){
                weakSelf.onDidChangeUserLocation([weakSelf locationInfoWithLocation:userLocation andAddress:address]);
            }
            weakSelf.onReverseGeoCodeResult = nil;
        };

        // 根据地理坐标获取地址信息
        if (!_geocodesearch) {
            _geocodesearch = [BMKGeoCodeSearch new];
        }
        _geocodesearch.delegate = weakSelf;
        BMKReverseGeoCodeSearchOption *reverseGeoCodeOption = [BMKReverseGeoCodeSearchOption new];
        reverseGeoCodeOption.location = userLocation.location.coordinate;
        BOOL flag = [_geocodesearch reverseGeoCode:reverseGeoCodeOption];
        if(flag)
        {
            NSLog(@"反geo检索发送成功");
        }
        else
        {
            if (self.onReverseGeoCodeResult) {
                self.onReverseGeoCodeResult(@{@"errorCode": @(-1), @"errorMessage": @"获取经纬度失败"});
            }
        }
    }
}

- (NSDictionary<NSString *,id> *)locationInfoWithLocation:(BMKUserLocation *)userLocation andAddress:(NSString *)address {
    CLLocation *location = userLocation.location;
    NSDictionary<NSString *,id> *locationEvent = @{
                                                   @"coords": @{
                                                           @"latitude": @(location.coordinate.latitude),
                                                           @"longitude": @(location.coordinate.longitude),
                                                           @"altitude": @(location.altitude),
                                                           @"accuracy": @(location.horizontalAccuracy),
                                                           @"altitudeAccuracy": @(location.verticalAccuracy),
                                                           @"heading": @(location.course),
                                                           @"speed": @(location.speed),
                                                           @"address": address ?: @"",
                                                           },
                                                   @"timestamp": @([location.timestamp timeIntervalSince1970] * 1000) // in ms
                                                   };
    return locationEvent;
}

- (NSDictionary<NSString *,id> *)locationInfoWithBMKLocation:(BMKLocation *)userLocation {
    CLLocation *location = userLocation.location;
    NSMutableString *address = [@"" mutableCopy];
    NSMutableDictionary *addressDetail = [NSMutableDictionary dictionary];
    if (userLocation.rgcData) {
        if (userLocation.rgcData.country) {
            [address appendString:userLocation.rgcData.country];
            [addressDetail setObject:userLocation.rgcData.country forKey:@"country"];
            [addressDetail setObject:userLocation.rgcData.countryCode ?: @"" forKey:@"countryCode"];
        }
        if (userLocation.rgcData.province) {
            [address appendString:userLocation.rgcData.province];
            [addressDetail setObject:userLocation.rgcData.province forKey:@"province"];
        }
        if (userLocation.rgcData.city) {
            [address appendString:userLocation.rgcData.city];
            [addressDetail setObject:userLocation.rgcData.city forKey:@"city"];
            [addressDetail setObject:userLocation.rgcData.cityCode ?: @"" forKey:@"cityCode"];
        }
        if (userLocation.rgcData.district) {
            [address appendString:userLocation.rgcData.district];
            [addressDetail setObject:userLocation.rgcData.district forKey:@"district"];
        }
        if (userLocation.rgcData.street) {
            [address appendString:userLocation.rgcData.street];
            [addressDetail setObject:userLocation.rgcData.street forKey:@"street"];
            [addressDetail setObject:userLocation.rgcData.streetNumber ?: @"" forKey:@"streetNumber"];
        }
    }
    NSDictionary<NSString *,id> *locationEvent = @{
                                                   @"coords": @{
                                                           @"latitude": @(location.coordinate.latitude),
                                                           @"longitude": @(location.coordinate.longitude),
                                                           @"altitude": @(location.altitude),
                                                           @"accuracy": @(location.horizontalAccuracy),
                                                           @"altitudeAccuracy": @(location.verticalAccuracy),
                                                           @"heading": @(location.course),
                                                           @"speed": @(location.speed),
                                                           @"address": address,
                                                           @"addressDetail": addressDetail,
                                                           },
                                                   @"timestamp": @([location.timestamp timeIntervalSince1970] * 1000) // in ms
                                                   };
    return locationEvent;
}

- (void)startUserLocation:(BaiduLocationOptions)options{
    if (!_locationService) {
        _locationService = [BaiduLocationKitService new];
    }
    _locationService.distanceFilter = options.distanceFilter;
    _locationService.desiredAccuracy = options.accuracy;
    _locationService.headingFilter = options.headingFilter;
    if (self.userTrackingMode == BMKUserTrackingModeHeading || self.userTrackingMode == BMKUserTrackingModeFollowWithHeading) {
        _locationService.enableHeadingUpdates = YES;
    }
    //
    __weak typeof (self) weakSelf = self;
    _locationService.didUpdateLocation = ^(BMKLocation * _Nullable location, NSError * _Nullable error) {
        //
        if (!error) {
            [weakSelf updateUserLocation:location];
        }
    };
    _locationService.didUpdateHeading = ^(BMKLocation * _Nullable location, CLHeading * _Nullable heading, NSError * _Nullable error) {
        //
        if (!error) {
            if (weakSelf.showsUserLocation) {
                BMKUserLocation *newUserLocation = [[CustomBMKUserLocation alloc] initWithBMKLocation:location andHeading:heading];
                [weakSelf updateLocationData:newUserLocation];
            }
            [weakSelf sendBMKLocationUserLocation:location];
        }
    };

    CLAuthorizationStatus checkStatus = [CLLocationManager authorizationStatus];
    if (checkStatus != kCLAuthorizationStatusDenied && checkStatus != kCLAuthorizationStatusNotDetermined) {
        [_locationService startUserLocationService];
    } else {
        [self requestAuthorization:@"whenInUse" completionHandler:^(CLAuthorizationStatus status) {
            if (status != kCLAuthorizationStatusDenied && status != kCLAuthorizationStatusNotDetermined) {
                [_locationService startUserLocationService];
                self.requestAuthorizationHandler = nil;
            } else {
                [BaiduLocationKitService requestLocationAuthorization:nil];
            }
        }];
    }
}

- (void)stopUserLocation{
    if(_locationService){
        [_locationService stopUserLocationService];
        _locationService = nil;
    }
}

- (void)requestAuthorization:(NSString*)type completionHandler:(void (^)(CLAuthorizationStatus))completionHandler
{
    int status = [CLLocationManager authorizationStatus];
    BOOL statusAuthorized = NO;
    switch (status) {
        case kCLAuthorizationStatusAuthorizedAlways:
        case kCLAuthorizationStatusAuthorizedWhenInUse:
            statusAuthorized = YES;
        case kCLAuthorizationStatusDenied:
        case kCLAuthorizationStatusRestricted:
        default:
            statusAuthorized = NO;
    }
    if (!statusAuthorized) {
        self.requestAuthorizationHandler = completionHandler;

        if (_locationManager == nil) {
            _locationManager = [[CLLocationManager alloc] init];
            _locationManager.delegate = self;
        }

        if ([type isEqualToString:@"always"]) {
            [_locationManager requestAlwaysAuthorization];
        } else {
            [_locationManager requestWhenInUseAuthorization];
        }
    } else {
        if (completionHandler) {
            completionHandler(status);
        }
    }
}

- (void)updateUserLocation:(BMKLocation *)userLocation {
    if (self.showsUserLocation) {
        BMKUserLocation *newUserLocation = [[CustomBMKUserLocation alloc] initWithBMKLocation:userLocation andHeading:nil];
        RCTExecuteOnMainQueue(^{
            [self updateLocationData:newUserLocation];
            if (!_firstUserLocated) {
                [self setCenterCoordinate:userLocation.location.coordinate];
                _firstUserLocated = YES; // 第一次定位到的时候就设置中心，之后不在这里设置
//                if (self.userTrackingMode == BMKUserTrackingModeNone) {
//                    [self stopUserLocation];
//                }
                if (self.onFirstUserLocated) {
                    self.onFirstUserLocated(newUserLocation);
                }
            }
        });

    }
    [self sendBMKLocationUserLocation:userLocation];
}

#pragma mark - CLLocationManagerDelegate
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    if (status != kCLAuthorizationStatusNotDetermined) {

        //for some reason, checking permission right away returns denied. need to wait a tiny bit
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            if (self.requestAuthorizationHandler) {
                self.requestAuthorizationHandler([CLLocationManager authorizationStatus]);
            }
        });
    }
}

#pragma mark - BMKLocationServiceDelegate

- (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation
{
    if (self.showsUserLocation) {
        RCTExecuteOnMainQueue(^{
            [self updateLocationData:userLocation];
            if (!_firstUserLocated) {
                [self setCenterCoordinate:userLocation.location.coordinate];
                _firstUserLocated = YES; // 第一次定位到的时候就设置中心，之后不在这里设置
//                if (self.userTrackingMode == BMKUserTrackingModeNone) {
//                    [self stopUserLocation];
//                }
                if (self.onFirstUserLocated) {
                    self.onFirstUserLocated(userLocation);
                }
            }
        });
        
    }
    [self sendUserLocation:userLocation isHeading:NO];
}

- (void)didUpdateUserHeading:(BMKUserLocation *)userLocation
{
    if (self.showsUserLocation) {
        [self updateLocationData:userLocation];
    }
    [self sendUserLocation:userLocation isHeading:YES];
}

#pragma mark -- BMKGeoCodeSearchDelegate
- (void)onGetGeoCodeResult:(BMKGeoCodeSearch *)searcher result:(BMKGeoCodeSearchResult *)result errorCode:(BMKSearchErrorCode)error
{
    if(self.onGeoCodeResult){
        
        //NSString *showmeg = [NSString stringWithFormat:@"纬度:%f,经度:%f",result.location.latitude,result.location.longitude];
        
        switch (error) {
            case BMK_SEARCH_NO_ERROR:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"address":_geoCodeSearchOption ? _geoCodeSearchOption.address : @"",
                                           @"location":@{
                                                   @"latitude": @(result.location.latitude),
                                                   @"longitude": @(result.location.longitude),
                                                   }});
                }
                
                break;
            case BMK_SEARCH_AMBIGUOUS_KEYWORD:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_AMBIGUOUS_KEYWORD),@"errorMessage":@"检索词有岐义"});
                }
                break;
            case BMK_SEARCH_AMBIGUOUS_ROURE_ADDR:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_AMBIGUOUS_ROURE_ADDR),@"errorMessage":@"检索地址有岐义"});
                }
                break;
            case BMK_SEARCH_NOT_SUPPORT_BUS:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_NOT_SUPPORT_BUS),@"errorMessage":@"该城市不支持公交搜索"});
                }
                break;
            case BMK_SEARCH_NOT_SUPPORT_BUS_2CITY:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_NOT_SUPPORT_BUS_2CITY),@"errorMessage":@"不支持跨城市公交"});
                }
                break;
            case BMK_SEARCH_RESULT_NOT_FOUND:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_RESULT_NOT_FOUND),@"errorMessage":@"没有找到检索结果"});
                }
                break;
            case BMK_SEARCH_ST_EN_TOO_NEAR:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_RESULT_NOT_FOUND),@"errorMessage":@"起终点太近"});
                }
                break;
            case BMK_SEARCH_KEY_ERROR:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_KEY_ERROR),@"errorMessage":@"key错误"});
                }
                break;
            case BMK_SEARCH_NETWOKR_ERROR:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_NETWOKR_ERROR),@"errorMessage":@"网络连接错误"});
                }
                break;
            case BMK_SEARCH_NETWOKR_TIMEOUT:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_NETWOKR_TIMEOUT),@"errorMessage":@"网络连接超时"});
                }
                break;
            case BMK_SEARCH_PERMISSION_UNFINISHED:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_PERMISSION_UNFINISHED),@"errorMessage":@"还未完成鉴权，请在鉴权通过后重试"});
                }
                break;
            case BMK_SEARCH_INDOOR_ID_ERROR:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_INDOOR_ID_ERROR),@"errorMessage":@"室内图ID错误"});
                }
                break;
            case BMK_SEARCH_FLOOR_ERROR:
                if (self.onGeoCodeResult) {
                    self.onGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_FLOOR_ERROR),@"errorMessage":@"室内图检索楼层错误"});
                }
                break;
                
            default:
                break;
        }
        
    }
    searcher.delegate = nil;
}

-(void)onGetReverseGeoCodeResult:(BMKGeoCodeSearch *)searcher result:(BMKReverseGeoCodeSearchResult *)result errorCode:(BMKSearchErrorCode)error
{
    if(self.onReverseGeoCodeResult){
        
        //NSString *showmeg = [NSString stringWithFormat:@"纬度:%f,经度:%f",result.location.latitude,result.location.longitude];
        /*
         /// 街道号码
         @property (nonatomic, strong) NSString* streetNumber;
         /// 街道名称
         @property (nonatomic, strong) NSString* streetName;
         /// 区县名称
         @property (nonatomic, strong) NSString* district;
         /// 城市名称
         @property (nonatomic, strong) NSString* city;
         /// 省份名称
         @property (nonatomic, strong) NSString* province;
         */
        
        switch (error) {
            case BMK_SEARCH_NO_ERROR:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"address":result.address,
                                                  @"location":@{
                                                          @"latitude": @(result.location.latitude),
                                                          @"longitude": @(result.location.longitude),
                                                          },
                                                  @"addressDetail":@{@"streetNumber":result.addressDetail.streetNumber,
                                                                     @"streetName":result.addressDetail.streetName,
                                                                     @"district":result.addressDetail.district,
                                                                     @"city":result.addressDetail.city,
                                                                     @"province":result.addressDetail.province
                                                                     }
                                                  //,@"poiList":result.poiList
                                                  });
                }
                
                break;
            case BMK_SEARCH_AMBIGUOUS_KEYWORD:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_AMBIGUOUS_KEYWORD),@"errorMessage":@"检索词有岐义"});
                }
                break;
            case BMK_SEARCH_AMBIGUOUS_ROURE_ADDR:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_AMBIGUOUS_ROURE_ADDR),@"errorMessage":@"检索地址有岐义"});
                }
                break;
            case BMK_SEARCH_NOT_SUPPORT_BUS:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_NOT_SUPPORT_BUS),@"errorMessage":@"该城市不支持公交搜索"});
                }
                break;
            case BMK_SEARCH_NOT_SUPPORT_BUS_2CITY:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_NOT_SUPPORT_BUS_2CITY),@"errorMessage":@"不支持跨城市公交"});
                }
                break;
            case BMK_SEARCH_RESULT_NOT_FOUND:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_RESULT_NOT_FOUND),@"errorMessage":@"没有找到检索结果"});
                }
                break;
            case BMK_SEARCH_ST_EN_TOO_NEAR:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_RESULT_NOT_FOUND),@"errorMessage":@"起终点太近"});
                }
                break;
            case BMK_SEARCH_KEY_ERROR:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_KEY_ERROR),@"errorMessage":@"key错误"});
                }
                break;
            case BMK_SEARCH_NETWOKR_ERROR:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_NETWOKR_ERROR),@"errorMessage":@"网络连接错误"});
                }
                break;
            case BMK_SEARCH_NETWOKR_TIMEOUT:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_NETWOKR_TIMEOUT),@"errorMessage":@"网络连接超时"});
                }
                break;
            case BMK_SEARCH_PERMISSION_UNFINISHED:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_PERMISSION_UNFINISHED),@"errorMessage":@"还未完成鉴权，请在鉴权通过后重试"});
                }
                break;
            case BMK_SEARCH_INDOOR_ID_ERROR:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_INDOOR_ID_ERROR),@"errorMessage":@"室内图ID错误"});
                }
                break;
            case BMK_SEARCH_FLOOR_ERROR:
                if (self.onReverseGeoCodeResult) {
                    self.onReverseGeoCodeResult(@{@"errorCode":@(BMK_SEARCH_FLOOR_ERROR),@"errorMessage":@"室内图检索楼层错误"});
                }
                break;
                
            default:
                break;
        }
        
    }
    searcher.delegate = nil;
}

#pragma mark - BMKMapViewDelegate

- (void)mapView:(RCTBaiduMapView *)mapView didSelectAnnotationView:(BMKAnnotationView *)view
{
    if (mapView.onPress && [view.annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        RCTBaiduMapAnnotation *annotation = (RCTBaiduMapAnnotation *)view.annotation;
        mapView.onPress(@{
                          @"action": @"annotation-click",
                          @"annotation": @{
                                  @"id": annotation.identifier,
                                  @"title": annotation.title ?: @"",
                                  @"subtitle": annotation.subtitle ?: @"",
                                  @"latitude": @(annotation.coordinate.latitude),
                                  @"longitude": @(annotation.coordinate.longitude)
                                  }
                          });
    }
    
    if ([view.annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        RCTBaiduMapAnnotation *annotation = (RCTBaiduMapAnnotation *)view.annotation;
        if (mapView.onAnnotationFocus) {
            mapView.onAnnotationFocus(@{
                                        @"annotationId": annotation.identifier
                                        });
        }
    }
}

- (void)mapView:(RCTBaiduMapView *)mapView didDeselectAnnotationView:(BMKAnnotationView *)view
{
    if ([view.annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        RCTBaiduMapAnnotation *annotation = (RCTBaiduMapAnnotation *)view.annotation;
        if (mapView.onAnnotationBlur) {
            mapView.onAnnotationBlur(@{
                                       @"annotationId": annotation.identifier
                                       });
        }
    }
}

- (void)mapView:(RCTBaiduMapView *)mapView annotationView:(BMKAnnotationView *)view didChangeDragState:(BMKAnnotationViewDragState)newState fromOldState:(BMKAnnotationViewDragState)oldState
{
    static NSArray *states;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        states = @[@"idle", @"starting", @"dragging", @"canceling", @"ending"];
    });
    
    if ([view.annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        RCTBaiduMapAnnotation *annotation = (RCTBaiduMapAnnotation *)view.annotation;
        if (mapView.onAnnotationDragStateChange) {
            mapView.onAnnotationDragStateChange(@{
                                                  @"state": states[newState],
                                                  @"oldState": states[oldState],
                                                  @"annotationId": annotation.identifier,
                                                  @"latitude": @(annotation.coordinate.latitude),
                                                  @"longitude": @(annotation.coordinate.longitude),
                                                  });
        }
    }
}

- (BMKAnnotationView *)mapView:(RCTBaiduMapView *)mapView viewForAnnotation:(RCTBaiduMapAnnotation *)annotation
{
    if (![annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        return nil;
    }
    
    BMKAnnotationView *annotationView;
    if (annotation.viewIndex != NSNotFound) {
        NSString *reuseIdentifier = NSStringFromClass([RCTBaiduMapAnnotationView class]);
        annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier];
        if (!annotationView) {
            annotationView = [[RCTBaiduMapAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
        }
        
        UIView *reactView = mapView.reactSubviews[annotation.viewIndex];
        ((RCTBaiduMapAnnotationView *)annotationView).contentView = reactView;
    }/* else if (annotation.image) {
      NSString *reuseIdentifier = NSStringFromClass([BMKAnnotationView class]);
      annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier];
      if (!annotationView) {
      annotationView = [[BMKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
      }
      annotationView.image = annotation.image;
      annotationView.centerOffset = CGPointMake(0, 0 - annotationView.image.size.height / 2);
      }*/ else {
          
          NSString *reuseIdentifier = NSStringFromClass([BMKPinAnnotationView class]);
          annotationView =
          [mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier] ?:
          [[BMKPinAnnotationView alloc] initWithAnnotation:annotation
                                           reuseIdentifier:reuseIdentifier];
          ((BMKPinAnnotationView *)annotationView).animatesDrop = annotation.animateDrop;
          
          //        ((BMKPinAnnotationView *)annotationView).pinColor = annotation.tintColor;
          
          
      }
    
    annotationView.canShowCallout = (annotation.title.length > 0);
    
    // 这个回调会早于 insertReactSubview 调用。。只要调用 addAnnotation 或 addAnnotations 方法就会触发
    if (annotation.leftCalloutViewIndex != NSNotFound) {
        NSArray<UIView *> *reactSubviews = mapView.reactSubviews;
        if (reactSubviews.count > annotation.leftCalloutViewIndex) {
            annotationView.leftCalloutAccessoryView = reactSubviews[annotation.leftCalloutViewIndex];
        }
    } else if (annotation.hasLeftCallout) {
        annotationView.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    } else {
        annotationView.leftCalloutAccessoryView = nil;
    }
    
    if (annotation.rightCalloutViewIndex != NSNotFound) {
        NSArray<UIView *> *reactSubviews = mapView.reactSubviews;
        if (reactSubviews.count >= annotation.rightCalloutViewIndex) {
            annotationView.rightCalloutAccessoryView = reactSubviews[annotation.rightCalloutViewIndex];
        }
    } else if (annotation.hasRightCallout) {
        annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    } else {
        annotationView.rightCalloutAccessoryView = nil;
    }
    
    annotationView.draggable = annotation.draggable;
    return annotationView;
}

- (BMKOverlayView *)mapView:(BMKMapView *)mapView viewForOverlay:(RCTBaiduMapOverlay *)overlay
{
    if ([overlay isKindOfClass:[RCTBaiduMapOverlay class]]) {
        BMKPolylineView *polylineView = [[BMKPolylineView alloc] initWithOverlay:overlay];
        polylineView.strokeColor = overlay.strokeColor;
        polylineView.lineWidth = overlay.lineWidth;
        return polylineView;
    }
    
    return nil;
}

- (void)mapView:(RCTBaiduMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
    [self _regionChanged:mapView];
    mapView.regionChangeObserveTimer = [NSTimer timerWithTimeInterval:RCTBaiduMapRegionChangeObserveInterval target:self selector:@selector(_onTick:) userInfo:@{RCTBaiduMapViewKey: mapView} repeats:YES];
    
    [[NSRunLoop mainRunLoop] addTimer:mapView.regionChangeObserveTimer forMode:NSRunLoopCommonModes];
}

- (void)mapView:(RCTBaiduMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
    [mapView.regionChangeObserveTimer invalidate];
    mapView.regionChangeObserveTimer = nil;
    
    [self _regionChanged:mapView];
    
    if (mapView.hasStartedRendering) {
        if (_shouldEmitRegionChange) {
            [self _emitRegionChangeEvent:mapView continuous:NO];
        } else {
            _shouldEmitRegionChange = YES; // 恢复正常状态
        }
    }
}

- (void)mapViewDidFinishLoading:(RCTBaiduMapView *)mapView
{
    mapView.hasStartedRendering = YES;
    if (mapView.autoZoomToSpan) {
        [mapView zoomToSpan];
    }
    //[self _emitRegionChangeEvent:mapView continuous:NO];
}

#pragma mark - Private

- (void)_onTick:(NSTimer *)timer
{
    [self _regionChanged:timer.userInfo[RCTBaiduMapViewKey]];
}

- (void)_regionChanged:(RCTBaiduMapView *)mapView
{
    BOOL needZoom = NO;
    CGFloat newLongitudeDelta = 0.0f;
    BMKCoordinateRegion region = mapView.region;
    
    if (!CLLocationCoordinate2DIsValid(region.center)) {
        return;
    }
    
    if (mapView.maxDelta > FLT_EPSILON && region.span.longitudeDelta > mapView.maxDelta) {
        needZoom = YES;
        newLongitudeDelta = mapView.maxDelta * (1 - RCTBaiduMapZoomBoundBuffer);
    } else if (mapView.minDelta > FLT_EPSILON && region.span.longitudeDelta < mapView.minDelta) {
        needZoom = YES;
        newLongitudeDelta = mapView.minDelta * (1 + RCTBaiduMapZoomBoundBuffer);
    }
    if (needZoom) {
        region.span.latitudeDelta = region.span.latitudeDelta / region.span.longitudeDelta * newLongitudeDelta;
        region.span.longitudeDelta = newLongitudeDelta;
        mapView.region = region;
    }
    
    [self _emitRegionChangeEvent:mapView continuous:YES];
}

- (void)_emitRegionChangeEvent:(RCTBaiduMapView *)mapView continuous:(BOOL)continuous
{
    if (mapView.onChange) {
        BMKCoordinateRegion region = mapView.region;
        if (!CLLocationCoordinate2DIsValid(region.center)) {
            return;
        }
        
        mapView.onChange(@{
                           @"continuous": @(continuous),
                           @"region": @{
                                   @"latitude": @(RCTZeroIfNaN(region.center.latitude)),
                                   @"longitude": @(RCTZeroIfNaN(region.center.longitude)),
                                   @"latitudeDelta": @(RCTZeroIfNaN(region.span.latitudeDelta)),
                                   @"longitudeDelta": @(RCTZeroIfNaN(region.span.longitudeDelta)),
                                   }
                           });
    }
}

@end
